/*
 * @文件描述:管理连接硬件相关,不需要手动实例化,已经挂在gameobject上,获取组件即可
 * @版本:
 * @作者:周晨阳
 * @Date:2021-09-19 11:03:54
 * 注意:其中的private方法不要试图更改为public去调用,因为这个类内部已经封装好了,外部只需要调用public方法
 * 就可以完成所有的操作,试图强行修改内部方法为public去调用可能会导致不可预知的错误
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System;
using System.Linq;
using UnityEngine.UI;
using Motor_Control;

namespace myConection
{
    // State object for reading client data asynchronously  
    class StateObject
    {
        // Size of receive buffer.  
        public const int BufferSize = 256;

        // Receive buffer.  
        public byte[] buffer = new byte[BufferSize];

        // Received data string.
        public StringBuilder sb = new StringBuilder();

        // Client socket.
        public Socket workSocket = null;
    }

    public class connection : MonoBehaviour
    {

        private static Socket clienSocket;
        private bool conState = false;
        private View view;
        private Analysis analysis;

        void Awake()
        {
            //conStateText = GameObject.Find("conState").GetComponent<Text>();
        }
        // Start is called before the first frame update
        /**
         * @brief :开始会自动进行一次连接，如果连接失败后续可以手动连接
         * @param {*}
         * @return {*}
         */
        void Start()
        {

            //获取view对象
            GameObject conObj = GameObject.Find("GameObject");
            view = (View)conObj.GetComponent(typeof(View));
            view.showInfo("连接状态：初始化");
            analysis = (Analysis)conObj.GetComponent(typeof(Analysis));
            connectToDevice();
        }

        //// Update is called once per frame
        void Update()
        {

        }

        /**
         * @brief :连接至硬件设备，通过异步socket连接
         * @param {*}
         * @return {int} 返回1时连接成功，否则连接失败，如果在已经连接的情况下调用则无效，返回1
         */
        public int connectToDevice()
        {
            if (clienSocket != null && clienSocket.Connected)
                return 1;
            view.showInfo("连接状态：正在连接");
            clienSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 88);
            try
            {
                //使用异步连接,不会导致堵塞
                clienSocket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), clienSocket);

            }
            catch (System.Exception)
            {
                Debug.Log("socket连接失败,是否已经连接了外部硬件？");
                view.showInfo("连接状态：连接失败，请尝试手动连接");
                conState = false;

                return 0;

            }
            return 1;

        }

        /// <summary>
        /// 连接硬件设备成功后的回调
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.  
                Socket client = (Socket)ar.AsyncState;

                // Complete the connection.  
                client.EndConnect(ar);

                //Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());
                Debug.Log("Socket已连接至" + client.RemoteEndPoint.ToString());
                view.showInfo("连接状态：连接成功");
                conState = true;

                //连接成功后会发送以下信息作为测试
                // Send(getWorkingSocket(), "This is a test text from client,you should see this after started client,otherwise the connection is wrong");
                // Send(getWorkingSocket(), "Client Sending time:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff"));

                //开始接受来自server的信息
                StartReceive(client);


            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Debug.Log(e.ToString());
                Debug.Log("socket连接失败！");
                view.showInfo("连接状态：连接失败，请尝试手动连接");
                conState = false;


            }
        }

        /// <summary>
        /// 断开socket连接
        /// </summary>
        public void disConnect()
        {
            clienSocket.Disconnect(false);
            clienSocket.Close();
            view.showInfo("连接状态：已断开");
            conState = false;

        }

        /// <summary>
        /// 获取当前在使用的socket对象
        /// </summary>
        /// <returns></returns>
        public static Socket getWorkingSocket()
        {
            return clienSocket;
        }

        /// <summary>
        /// 开始接收数据,调用后会一直处于接收状态,
        /// </summary>
        /// <param name="client"></param>
        private void StartReceive(Socket client)
        {
            try
            {
                // Create the state object.  
                StateObject state = new StateObject();
                state.workSocket = client;
                // Begin receiving the data from the remote device.  
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Debug.Log(e.ToString());
            }
        }

        /// <summary>
        /// 接收到数据后的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void ReceiveCallback(IAsyncResult ar)
        {

            try
            {
                // Retrieve the state object and the client socket
                // from the asynchronous state object.  
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;
                String content = String.Empty;

                // Read data from the remote device.  
                int bytesRead = client.EndReceive(ar);
                // There might be more data, so store the data received so far.  
                //state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));
                if (bytesRead > 0)
                {
                    // There  might be more data, so store the data received so far.
                    state.sb.Clear();
                    state.sb.Append(Encoding.UTF8.GetString(
                        state.buffer, 0, bytesRead));


                    // Check for end-of-file tag. If it is not there, read
                    // more data.  
                    content = state.sb.ToString();
                    if (content.IndexOf("<EOF>") > -1)
                    {
                        // All the data has been read from the
                        // client. Display it on the console.  
                        // Console.WriteLine("Read {0} bytes from Server. \n Data : {1}",
                        //     content.Length, content);

                        //Debug.Log("Read " + content.Length + "bytes from Server. \n Data : " + content);
                        //Debug.Log("client time:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff"));
                        if (analysis == null)
                        {
                            GameObject conObj = GameObject.Find("GameObject");
                            analysis = (Analysis)conObj.GetComponent(typeof(Analysis));

                        }
                        string[] stringSeparators = new string[] { "<EOF>" };
                        string[] strings = content.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
                        if (strings.Length > 3)
                        {
                            //到这里加入解析字符串的步骤
                            for (int i = 1; i < strings.Length - 1; i++)
                            {
                                //Debug.Log("string:" + strings[i]);
                                Analysis.analysisString(strings[i]);
                            }
                        }
                        else
                        {
                            //到这里加入解析字符串的步骤
                            for (int i = 0; i < strings.Length; i++)
                            {
                                //Debug.Log("string:" + strings[i]);
                                Analysis.analysisString(strings[i]);
                            }
                        }


                        //重复接受来自客户端的数据
                        state.sb.Clear();
                        state.buffer = new byte[StateObject.BufferSize];
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);//从已连接的Socket中开始异步地接收数据
                    }
                    else
                    {
                        // Not all data received. Get more.  
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);
                    }
                }

            }
            catch (Exception e)
            {
                //Console.WriteLine(e.ToString());
                Debug.Log(e.ToString());
            }
        }
        /// <summary>
        /// 通过socket发送字符串
        /// </summary>
        /// <param name="client">socket对象</param>
        /// <param name="data">字符串</param>
        public static void Send(Socket client, String data)
        {
            // if (!conState)
            // {
            //     return;
            // }
            // Convert the string data to byte data using ASCII encoding.  
            byte[] byteData = Encoding.UTF8.GetBytes(data + "<EOF>");

            // Begin sending the data to the remote device.  
            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), client);
        }

        /// <summary>
        /// 发送数据成功后的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.  
                Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.  
                int bytesSent = client.EndSend(ar);
                //Console.WriteLine("Sent {0} bytes to server.", bytesSent);
                //Debug.Log("Sent" + bytesSent + "bytes to server.");
                // Signal that all bytes have been sent.  
                //sendDone.Set();
            }
            catch (Exception e)
            {
                //Console.WriteLine(e.ToString());
                Debug.Log(e.ToString());

            }
        }

    }
}
